{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# ProjectQ Mapper Tutorial"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The aim of this short tutorial is to give an introduction to the ProjectQ mappers.\n",
    "\n",
    "ProjectQ allows a user to write a quantum program in a high-level language. For example, one can apply quantum operations on n-qubits, e.g., `QFT`, and the compiler will decompose this operations into two-qubit and single-qubit gates. See the [compiler_tutorial](https://github.com/ProjectQ-Framework/ProjectQ/tree/develop/examples) for an introduction.\n",
    "\n",
    "After decomposing a quantum program into two-qubit and single-qubit gates which a quantum computer supports, we have to take the physical layout of these qubits into account. Two-qubit gates are only possible if the qubits are next to each other. For example the qubits could be arranged in a linear chain or a two-dimensional grid and only nearest neighbour qubits can perform a two-qubit gate. ProjectQ uses **mappers** which move the positions of the qubits close to each other using `Swap` operations in order that we can execute a two-qubit gate.\n",
    "\n",
    "The implementation and some results of ProjectQ's mappers are discussed in [our paper (section 3C)](https://arxiv.org/abs/1806.01861)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example: Mapping to a linear chain\n",
    "\n",
    "Let's look at an example of a quantum fourier transform (`QFT`) compiled into single-qubit gates and `CNOT`s. First, we look at the resources required if the qubits have an *all-to-all* connectivity, i.e., any pairs of qubits can execute a `CNOT`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Gate class counts:\n",
      "    AllocateQubitGate : 15\n",
      "    CXGate : 210\n",
      "    HGate : 15\n",
      "    R : 105\n",
      "    Rz : 210\n",
      "\n",
      "Gate counts:\n",
      "    Allocate : 15\n",
      "    CX : 210\n",
      "    H : 15\n",
      "    R(0.000191747598) : 2\n",
      "    R(0.000383495197) : 3\n",
      "    R(0.000766990394) : 4\n",
      "    R(0.001533980788) : 5\n",
      "    R(0.003067961576) : 6\n",
      "    R(0.006135923151) : 7\n",
      "    R(0.012271846303) : 8\n",
      "    R(0.024543692606) : 9\n",
      "    R(0.049087385213) : 10\n",
      "    R(0.098174770424) : 11\n",
      "    R(0.196349540849) : 12\n",
      "    R(0.392699081698) : 13\n",
      "    R(0.785398163398) : 14\n",
      "    R(9.5873799e-05) : 1\n",
      "    Rz(0.000191747598) : 2\n",
      "    Rz(0.000383495197) : 3\n",
      "    Rz(0.000766990394) : 4\n",
      "    Rz(0.001533980788) : 5\n",
      "    Rz(0.003067961576) : 6\n",
      "    Rz(0.006135923151) : 7\n",
      "    Rz(0.012271846303) : 8\n",
      "    Rz(0.024543692606) : 9\n",
      "    Rz(0.049087385213) : 10\n",
      "    Rz(0.098174770424) : 11\n",
      "    Rz(0.196349540849) : 12\n",
      "    Rz(0.392699081698) : 13\n",
      "    Rz(0.785398163398) : 14\n",
      "    Rz(11.780972451) : 14\n",
      "    Rz(12.1736715327) : 13\n",
      "    Rz(12.3700210735) : 12\n",
      "    Rz(12.4681958439) : 11\n",
      "    Rz(12.5172832291) : 10\n",
      "    Rz(12.5418269218) : 9\n",
      "    Rz(12.5540987681) : 8\n",
      "    Rz(12.5602346912) : 7\n",
      "    Rz(12.5633026528) : 6\n",
      "    Rz(12.5648366336) : 5\n",
      "    Rz(12.565603624) : 4\n",
      "    Rz(12.5659871192) : 3\n",
      "    Rz(12.5661788668) : 2\n",
      "    Rz(12.5662747406) : 1\n",
      "    Rz(9.5873799e-05) : 1\n",
      "\n",
      "Max. width (number of qubits) : 15.\n"
     ]
    }
   ],
   "source": [
    "import projectq\n",
    "from projectq.backends import ResourceCounter\n",
    "from projectq.ops import CNOT, QFT\n",
    "from projectq.setups import restrictedgateset\n",
    "\n",
    "engine_list = restrictedgateset.get_engine_list(one_qubit_gates=\"any\",\n",
    "                                                two_qubit_gates=(CNOT,))\n",
    "resource_counter = ResourceCounter()\n",
    "eng = projectq.MainEngine(backend=resource_counter, engine_list=engine_list)\n",
    "\n",
    "qureg = eng.allocate_qureg(15)\n",
    "QFT | qureg\n",
    "eng.flush()\n",
    "\n",
    "print(resource_counter)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's assume our qubits are arrange on a linear chain, we can use an already [predefined compiler setup](http://projectq.readthedocs.io/en/latest/projectq.setups.html#module-projectq.setups.linear) to compile to this architecture:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Gate class counts:\n",
      "    AllocateQubitGate : 15\n",
      "    CXGate : 888\n",
      "    HGate : 15\n",
      "    R : 105\n",
      "    Rz : 210\n",
      "\n",
      "Gate counts:\n",
      "    Allocate : 15\n",
      "    CX : 888\n",
      "    H : 15\n",
      "    R(0.000191747598) : 2\n",
      "    R(0.000383495197) : 3\n",
      "    R(0.000766990394) : 4\n",
      "    R(0.001533980788) : 5\n",
      "    R(0.003067961576) : 6\n",
      "    R(0.006135923151) : 7\n",
      "    R(0.012271846303) : 8\n",
      "    R(0.024543692606) : 9\n",
      "    R(0.049087385213) : 10\n",
      "    R(0.098174770424) : 11\n",
      "    R(0.196349540849) : 12\n",
      "    R(0.392699081698) : 13\n",
      "    R(0.785398163398) : 14\n",
      "    R(9.5873799e-05) : 1\n",
      "    Rz(0.000191747598) : 2\n",
      "    Rz(0.000383495197) : 3\n",
      "    Rz(0.000766990394) : 4\n",
      "    Rz(0.001533980788) : 5\n",
      "    Rz(0.003067961576) : 6\n",
      "    Rz(0.006135923151) : 7\n",
      "    Rz(0.012271846303) : 8\n",
      "    Rz(0.024543692606) : 9\n",
      "    Rz(0.049087385213) : 10\n",
      "    Rz(0.098174770424) : 11\n",
      "    Rz(0.196349540849) : 12\n",
      "    Rz(0.392699081698) : 13\n",
      "    Rz(0.785398163398) : 14\n",
      "    Rz(11.780972451) : 14\n",
      "    Rz(12.1736715327) : 13\n",
      "    Rz(12.3700210735) : 12\n",
      "    Rz(12.4681958439) : 11\n",
      "    Rz(12.5172832291) : 10\n",
      "    Rz(12.5418269218) : 9\n",
      "    Rz(12.5540987681) : 8\n",
      "    Rz(12.5602346912) : 7\n",
      "    Rz(12.5633026528) : 6\n",
      "    Rz(12.5648366336) : 5\n",
      "    Rz(12.565603624) : 4\n",
      "    Rz(12.5659871192) : 3\n",
      "    Rz(12.5661788668) : 2\n",
      "    Rz(12.5662747406) : 1\n",
      "    Rz(9.5873799e-05) : 1\n",
      "\n",
      "Max. width (number of qubits) : 15.\n"
     ]
    }
   ],
   "source": [
    "from projectq.setups import linear\n",
    "\n",
    "engine_list2 = linear.get_engine_list(num_qubits=15, cyclic=False,\n",
    "                                      one_qubit_gates=\"any\",\n",
    "                                      two_qubit_gates=(CNOT,))\n",
    "resource_counter2 = ResourceCounter()\n",
    "eng2 = projectq.MainEngine(backend=resource_counter2, engine_list=engine_list2)\n",
    "\n",
    "qureg2 = eng2.allocate_qureg(15)\n",
    "QFT | qureg2\n",
    "eng2.flush()\n",
    "\n",
    "print(resource_counter2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One can see that once we restricted the hardware to a linear chain, the same program requires a lot more `CNOT` (also called `CX`) gates. This is due to additionals `Swap` operations to move the qubits around (a `Swap` gate can be constructed out of three `CX` gates)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example: Mapping to a two-dimensional grid\n",
    "\n",
    "ProjectQ also has a [predefined setup](http://projectq.readthedocs.io/en/latest/projectq.setups.html#module-projectq.setups.grid) to map to a two-dimensional grid."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Gate class counts:\n",
      "    AllocateQubitGate : 15\n",
      "    CXGate : 741\n",
      "    HGate : 15\n",
      "    R : 105\n",
      "    Rz : 210\n",
      "\n",
      "Gate counts:\n",
      "    Allocate : 15\n",
      "    CX : 741\n",
      "    H : 15\n",
      "    R(0.000191747598) : 2\n",
      "    R(0.000383495197) : 3\n",
      "    R(0.000766990394) : 4\n",
      "    R(0.001533980788) : 5\n",
      "    R(0.003067961576) : 6\n",
      "    R(0.006135923151) : 7\n",
      "    R(0.012271846303) : 8\n",
      "    R(0.024543692606) : 9\n",
      "    R(0.049087385213) : 10\n",
      "    R(0.098174770424) : 11\n",
      "    R(0.196349540849) : 12\n",
      "    R(0.392699081698) : 13\n",
      "    R(0.785398163398) : 14\n",
      "    R(9.5873799e-05) : 1\n",
      "    Rz(0.000191747598) : 2\n",
      "    Rz(0.000383495197) : 3\n",
      "    Rz(0.000766990394) : 4\n",
      "    Rz(0.001533980788) : 5\n",
      "    Rz(0.003067961576) : 6\n",
      "    Rz(0.006135923151) : 7\n",
      "    Rz(0.012271846303) : 8\n",
      "    Rz(0.024543692606) : 9\n",
      "    Rz(0.049087385213) : 10\n",
      "    Rz(0.098174770424) : 11\n",
      "    Rz(0.196349540849) : 12\n",
      "    Rz(0.392699081698) : 13\n",
      "    Rz(0.785398163398) : 14\n",
      "    Rz(11.780972451) : 14\n",
      "    Rz(12.1736715327) : 13\n",
      "    Rz(12.3700210735) : 12\n",
      "    Rz(12.4681958439) : 11\n",
      "    Rz(12.5172832291) : 10\n",
      "    Rz(12.5418269218) : 9\n",
      "    Rz(12.5540987681) : 8\n",
      "    Rz(12.5602346912) : 7\n",
      "    Rz(12.5633026528) : 6\n",
      "    Rz(12.5648366336) : 5\n",
      "    Rz(12.565603624) : 4\n",
      "    Rz(12.5659871192) : 3\n",
      "    Rz(12.5661788668) : 2\n",
      "    Rz(12.5662747406) : 1\n",
      "    Rz(9.5873799e-05) : 1\n",
      "\n",
      "Max. width (number of qubits) : 15.\n"
     ]
    }
   ],
   "source": [
    "from projectq.setups import grid\n",
    "\n",
    "engine_list3 = grid.get_engine_list(num_rows=3, num_columns=5,\n",
    "                                      one_qubit_gates=\"any\",\n",
    "                                      two_qubit_gates=(CNOT,))\n",
    "resource_counter3 = ResourceCounter()\n",
    "eng3 = projectq.MainEngine(backend=resource_counter3, engine_list=engine_list3)\n",
    "\n",
    "qureg3 = eng3.allocate_qureg(15)\n",
    "QFT | qureg3\n",
    "eng3.flush()\n",
    "\n",
    "print(resource_counter3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that mapping a `QFT` to a two-dimensional grid layout requires fewer `CX` gates than mapping to a linear chain as expected."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Inspecting the current mapping of logical qubits to physical qubits\n",
    "\n",
    "A qubit which you obtain by calling the `allocate_qubit()` function of the compiler (`MainEngine`) is just an abstract objects which has a unique ID."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "This logical qubit0 has the unique ID: 10\n",
      "This logical qubit1 has the unique ID: 11\n",
      "This logical qubit2 has the unique ID: 12\n",
      "Allocate | Qureg[0]\n",
      "X | Qureg[0]\n",
      "Allocate | Qureg[1]\n",
      "Allocate | Qureg[2]\n"
     ]
    }
   ],
   "source": [
    "from projectq.backends import CommandPrinter\n",
    "from projectq.ops import X, Swap\n",
    "\n",
    "engine_list4 = linear.get_engine_list(num_qubits=3, cyclic=False,\n",
    "                                      one_qubit_gates=\"any\",\n",
    "                                      two_qubit_gates=(CNOT, Swap))\n",
    "\n",
    "eng4 = projectq.MainEngine(backend=CommandPrinter(), engine_list=engine_list4)\n",
    "\n",
    "# For instructional purposes we change that the eng4 gives logical ids starting\n",
    "# from 10. This could e.g. be the case if a previous part of the program\n",
    "# already allocated 10 qubits\n",
    "eng4._qubit_idx = 10\n",
    "\n",
    "qubit0 = eng4.allocate_qubit()\n",
    "qubit1 = eng4.allocate_qubit()\n",
    "qubit2 = eng4.allocate_qubit()\n",
    "\n",
    "X | qubit0\n",
    "\n",
    "# Remember that allocate_qubit returns a quantum register (Qureg) of size 1,\n",
    "# so accessing the qubit requires qubit[0]\n",
    "print(\"This logical qubit0 has the unique ID: {}\".format(qubit0[0].id))\n",
    "print(\"This logical qubit1 has the unique ID: {}\".format(qubit1[0].id))\n",
    "print(\"This logical qubit2 has the unique ID: {}\".format(qubit2[0].id)) \n",
    "\n",
    "eng4.flush()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As we can see `qubit0` has a logical ID equal to 10. The *LinearMapper* in this compiler setup then places these qubits on a linear chain with the following physical qubit ID ordering:\n",
    "\n",
    "0 -- 1 -- 2\n",
    "\n",
    "where -- indicates that these two qubits can perform a `CNOT` gate. If you are interested in knowing where a specific logical qubit is currently placed, you can access this information via the `current_mapping` property of the mapper:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Physical location of qubit0: 0\n",
      "Physical location of qubit1: 1\n",
      "Physical location of qubit2: 2\n"
     ]
    }
   ],
   "source": [
    "# eng.mapper gives back the mapper in the engine_list\n",
    "current_mapping = eng4.mapper.current_mapping\n",
    "# current_mapping is a dictionary with keys being the\n",
    "# logical qubit ids and the values being the physical ids on\n",
    "# on the linear chain\n",
    "print(\"Physical location of qubit0: {}\".format(current_mapping[qubit0[0].id]))\n",
    "print(\"Physical location of qubit1: {}\".format(current_mapping[qubit1[0].id]))\n",
    "print(\"Physical location of qubit2: {}\".format(current_mapping[qubit2[0].id]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Suppose we now perform a `CNOT` between `qubit0` and `qubit2`, then the mapper needs to swap these two qubits close to each other to perform the operation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Swap | ( Qureg[1], Qureg[2] )\n",
      "CX | ( Qureg[0], Qureg[1] )\n",
      "\n",
      "Physical location of qubit0: 0\n",
      "Physical location of qubit1: 2\n",
      "Physical location of qubit2: 1\n"
     ]
    }
   ],
   "source": [
    "CNOT | (qubit0, qubit2)\n",
    "eng4.flush()\n",
    "# Get current mapping:\n",
    "current_mapping = eng4.mapper.current_mapping\n",
    "print(\"\\nPhysical location of qubit0: {}\".format(current_mapping[qubit0[0].id]))\n",
    "print(\"Physical location of qubit1: {}\".format(current_mapping[qubit1[0].id]))\n",
    "print(\"Physical location of qubit2: {}\".format(current_mapping[qubit2[0].id]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see that the compiler added a `Swap` gate to change the location of the logical qubits in this chain so that the CNOT can be performed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Measurements, probabilities, and amplitudes\n",
    "\n",
    "While the compiler automatically remaps logical qubits to different physical locations, how does this affect the high-level programmer?\n",
    "\n",
    "The short answer is not at all. \n",
    "\n",
    "If you want to measure a logical qubit, just apply a measurement gate as before and the compiler will automatically find the correct physical qubit to measure:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "qubit0 was measured in state: 1\n"
     ]
    }
   ],
   "source": [
    "from projectq.backends import Simulator\n",
    "from projectq.ops import Measure\n",
    "\n",
    "engine_list5 = linear.get_engine_list(num_qubits=3, cyclic=False,\n",
    "                                      one_qubit_gates=\"any\",\n",
    "                                      two_qubit_gates=(CNOT, Swap))\n",
    "\n",
    "eng5 = projectq.MainEngine(backend=Simulator(), engine_list=engine_list5)\n",
    "\n",
    "qubit0 = eng5.allocate_qubit()\n",
    "qubit1 = eng5.allocate_qubit()\n",
    "qubit2 = eng5.allocate_qubit()\n",
    "\n",
    "X | qubit0\n",
    "Measure | qubit0\n",
    "eng5.flush()\n",
    "\n",
    "print(\"qubit0 was measured in state: {}\".format(int(qubit0)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All the simulator functionalities, e.g., `get_probability` or `get_amplitude` work as usual because they take logical qubits as arguments so the programmer does not need to worry about at which physical location `qubit0` is at the moment:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.0"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eng5.backend.get_probability('1', qubit0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
